import { message } from 'antd/lib';

export type Callback = (e: Event) => void;
export type SocketCallBack<RT> = (e: RT) => void;

interface Result<RT> {
  code: number;
  data: any;
  msg: string;
}
interface SocketProp<RT> {
  // 链接的通道的地址
  url: string | null;
  // 心跳时间间隔
  heartTime?: number;
  // 心跳信息,默认为'ping'
  heartMsg?: string;
  // 是否自动重连
  isReconnect?: boolean;
  // 是否销毁
  isReStory?: boolean;
  // 重连时间间隔
  reconnectTime?: number;
  // 重连次数 -1 则不限制
  reconnectCount?: number;
  // 连接成功的回调
  openCb?: Callback;
  // 关闭的回调
  closeCb?: Callback;
  // 消息的回调
  messageCb?: SocketCallBack<RT>;
  // 错误的回调
  errorCb?: Callback;
}

/**
 * 心跳基类
 */

export class Heart {
  heartTimeOut!: number; // 心跳计时器

  ServerHeartTimeOut!: number; // 心跳计时器

  timeout = 5000;

  // 重置
  reset(): void {
    clearTimeout(this.heartTimeOut);
    clearTimeout(this.ServerHeartTimeOut);
  }

  /**
   * 启动心跳
   * @param {Function} cb 回调函数
   */
  start(cb: Callback): void {
    this.heartTimeOut = setTimeout((e: Event) => {
      cb(e);
      this.ServerHeartTimeOut = setTimeout((e: Event) => {
        cb(e);
        // 重新开始检测
        this.reset();
        this.start(cb);
      }, this.timeout);
    }, this.timeout);
  }
}

export default class Socket<T, RT> extends Heart {
  ws!: WebSocket;

  reconnectTimer: any = 0; // 重连计时器

  reconnectCount = 10; // 变量保存，防止丢失

  options: SocketProp<RT> = {
    // 链接的通道的地址
    url: null,
    // 心跳时间间隔
    heartTime: 100000,
    // 心跳信息,默认为ping'
    heartMsg: 'ping',
    // 是否自动重连
    isReconnect: true,
    // 是否销毁
    isReStory: false,
    // 重连时间间隔
    reconnectTime: 5000,
    // 重连次数 -1 则不限制
    reconnectCount: 5,
    openCb: (e: Event) => {
      console.log('[WebSocket] 连接成功的默认回调::::', e);
    }, // 连接成功的回调
    closeCb: (e: Event) => {
      console.log('[WebSocket] 关闭的默认回调::::', e);
    }, // 关闭的回调
    messageCb: (e: RT) => {
      console.log('[WebSocket] 连接成功的默认回调::::', e);
    }, // 消息的回调
    errorCb: (e: Event) => {
      console.log('[WebSocket] 错误的默认回调::::', e);
    }, // 错误的回调
  };

  constructor(ops: SocketProp<RT>) {
    super();
    Object.assign(this.options, ops);
    this.create();
  }

  /**
   * 建立连接
   */
  create(): void {
    if (!('WebSocket' in window)) {
      throw new Error('当前浏览器不支持，无法使用');
    }
    if (!this.options.url) {
      throw new Error('地址不存在，无法建立通道');
    }
    // this.ws = null
    this.ws = new WebSocket(this.options.url);
    this.onopen(this.options.openCb as Callback);
    this.onclose(this.options.closeCb as Callback);
    this.onmessage(this.options.messageCb as SocketCallBack<RT>);
  }

  /**
   * 自定义连接成功事件
   * 如果callback存在，调用callback，不存在调用OPTIONS中的回调
   * @param {Function} callback 回调函数
   */
  onopen(callback: Callback): void {
    this.ws.onopen = event => {
      // 清除重连定时器
      clearTimeout(this.reconnectTimer);
      // 计数器重置
      this.options.reconnectCount = this.reconnectCount;
      // 建立心跳机制
      super.reset();
      super.start(() => {
        this.send(this.options.heartMsg as string);
      });
      if (typeof callback === 'function') {
        callback(event);
      } else {
        typeof this.options.openCb === 'function' && this.options.openCb(event);
      }
    };
  }

  /**
   * 自定义关闭事件
   * 如果callback存在，调用callback，不存在调用OPTIONS中的回调
   * @param {Function} callback 回调函数
   */
  onclose(callback: Callback): void {
    this.ws.onclose = event => {
      super.reset();
      !this.options.isReStory && this.onreconnect();
      if (typeof callback === 'function') {
        callback(event);
      } else {
        typeof this.options.closeCb === 'function' && this.options.closeCb(event);
      }
    };
  }

  /**
   * 自定义错误事件
   * 如果callback存在，调用callback，不存在调用OPTIONS中的回调
   * @param {Function} callback 回调函数
   */
  onerror(callback: Callback): void {
    this.ws.onerror = event => {
      if (typeof callback === 'function') {
        callback(event);
      } else {
        typeof this.options.errorCb === 'function' && this.options.errorCb(event);
      }
    };
  }

  /**
   * 自定义消息监听事件
   * 如果callback存在，调用callback，不存在调用OPTIONS中的回调
   * @param {Function} callback 回调函数
   */
  onmessage(callback: SocketCallBack<RT>): void {
    this.ws.onmessage = (event: MessageEvent<string>) => {
      const strMessage = event.data;
      const { code, data, msg }: Result<RT> = JSON.parse(strMessage);
      if (code === 200) {
        // 收到任何消息，重新开始倒计时心跳检测
        super.reset();
        super.start(() => {
          this.send(this.options.heartMsg as string);
        });
        console.log(data, 'onmessage');
        if (typeof callback === 'function') {
          callback(data);
        } else {
          typeof this.options.messageCb === 'function' && this.options.messageCb(data);
        }
      } else {
        message.error(msg || '收到失败的数据！');
      }
    };
  }

  /**
   * 自定义发送消息事件
   * @param {String} data 发送的文本
   */
  send(data: T | string): void {
    if (this.ws.readyState !== this.ws.OPEN) {
      throw new Error('没有连接到服务器，无法推送');
    }
    data = JSON.stringify(data);
    this.ws.send(data);
  }

  /**
   * 连接事件
   */
  onreconnect(): void {
    if ((this.options.reconnectCount as number) > 0 || this.options.reconnectCount === -1) {
      this.reconnectTimer = setTimeout(() => {
        this.create();
        if (this.options.reconnectCount !== -1) {
          (this.options.reconnectCount as number)--;
        }
      }, this.options.reconnectTime);
    } else {
      clearTimeout(this.reconnectTimer);
      this.options.reconnectCount = this.reconnectCount;
    }
  }

  /**
   * 销毁
   */
  destroy(): void {
    super.reset();
    // 清除重连定时器
    clearTimeout(this.reconnectTimer);
    this.options.isReStory = true;
    this.ws.close();
  }
}
